package OBJ;

import org.joml.Matrix4f;
import org.joml.Vector3f;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL20;
import org.lwjgl.system.MemoryStack;

import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;

import static org.lwjgl.opengl.GL20.*;


//着色器程序类
public class ModelShaderProgram {
    public final int programId;
    private static final String VERTEX_SHADER_PATH = "vertex.glsl";     // 顶点着色器路径
    private static final String FRAGMENT_SHADER_PATH = "fragment.glsl"; // 片段着色器路径
    public ModelShaderProgram(String vertexShaderCode, String fragmentShaderCode) {

        programId = glCreateProgram();
        int vertexShader = createShader(vertexShaderCode, GL_VERTEX_SHADER);
        int fragmentShader = createShader(fragmentShaderCode, GL_FRAGMENT_SHADER);
        glAttachShader(programId, vertexShader);
        glAttachShader(programId, fragmentShader);
        glLinkProgram(programId);

        checkCompileErrors(programId, "PROGRAM");

        //printAllUniforms(programId);
        glDeleteShader(vertexShader);
        glDeleteShader(fragmentShader);
    }

    //创建着色器
    private int createShader(String shaderCode, int shaderType) {
        int shader = glCreateShader(shaderType);
        glShaderSource(shader, shaderCode);
        glCompileShader(shader);
        checkCompileErrors(shader, shaderType == GL_VERTEX_SHADER ? "VERTEX" : "FRAGMENT");
        return shader;
    }
    //检查编译错误
    private void checkCompileErrors(int object, String type) {
        if (type.equals("PROGRAM")) {
            int success = glGetProgrami(object, GL_LINK_STATUS);
            if (success == GL_FALSE) {
                String log = glGetProgramInfoLog(object);
                System.err.println("程序链接错误 (" + type + "):\n" + log);
                System.exit(-1);
            }
        } else {
            int success = glGetShaderi(object, GL_COMPILE_STATUS);
            if (success == GL_FALSE) {
                String log = glGetShaderInfoLog(object);
                System.err.println("着色器编译错误 (" + type + "):\n" + log);
                System.exit(-1);
            }
        }
    }
    //打印所有uniform变量
    private void printAllUniforms(int programId) {
        // 获取当前着色器中的 uniform 变量数量
        IntBuffer uniformCount = BufferUtils.createIntBuffer(1);
        GL20.glGetProgramiv(programId, GL20.GL_ACTIVE_UNIFORMS, uniformCount);
        int count = uniformCount.get(0);
        System.out.println("Active Uniforms: " + count);

        // 获取最大 uniform 名称长度
        IntBuffer maxNameLengthBuffer = BufferUtils.createIntBuffer(1);
        GL20.glGetProgramiv(programId, GL20.GL_ACTIVE_UNIFORM_MAX_LENGTH, maxNameLengthBuffer);
        int maxUniformNameLength = maxNameLengthBuffer.get(0);

        // 分配足够大的缓冲区
        ByteBuffer nameBuffer = BufferUtils.createByteBuffer(maxUniformNameLength);
        IntBuffer lengthBuffer = BufferUtils.createIntBuffer(1);
        IntBuffer sizeBuffer = BufferUtils.createIntBuffer(1);
        IntBuffer typeBuffer = BufferUtils.createIntBuffer(1);

        // 遍历所有 uniform 变量
        for (int i = 0; i < count; i++) {
            GL20.glGetActiveUniform(programId, i, lengthBuffer, sizeBuffer, typeBuffer, nameBuffer);

            // 读取 uniform 名称
            int length = lengthBuffer.get(0);
            byte[] nameBytes = new byte[length];
            nameBuffer.get(nameBytes, 0, length);
            nameBuffer.rewind();
            String name = new String(nameBytes);

            // 获取 uniform 变量的位置
            int location = GL20.glGetUniformLocation(programId, name);

            // 打印 uniform 变量信息
            System.out.printf("Uniform #%d: Name=%s, Type=0x%04X, Size=%d, Location=%d\n",
                    i, name, typeBuffer.get(0), sizeBuffer.get(0), location);
        }
    }
    //使用着色器
    public void use() {
        glUseProgram(programId);
    }
    //设置矩阵uniform变量
    public void setMat4(String name, Matrix4f value) {
        try (MemoryStack stack = MemoryStack.stackPush()) {
            glUniformMatrix4fv(glGetUniformLocation(programId, name), false,
                    value.get(stack.mallocFloat(16)));
        }
        try (MemoryStack stack = MemoryStack.stackPush()) {
            FloatBuffer buffer = stack.mallocFloat(16);
            value.get(buffer);
            glUniformMatrix4fv(glGetUniformLocation(programId, name), false, buffer);
        }
    }

    // 在ShaderProgram类中添加以下方法：
    public void setUniform(String name, int value) {
        int location = glGetUniformLocation(programId, name);
        if (location == -1) {
            System.err.println("警告：未找到uniform变量 '" + name + "'");
            return;
        }
        glUniform1i(location, value);
    }
    public void setUniform(String name, float value) {
        int location = glGetUniformLocation(programId, name);
        if (location == -1) return;
        glUniform1f(location, value);
    }
    public void setUniform(String name, Vector3f value) {
        int location = glGetUniformLocation(programId, name);
        if (location == -1) return;
        glUniform3f(location, value.x, value.y, value.z);
    }
    public void setUniform(String name, boolean value) {
        int location = glGetUniformLocation(programId, name);
        if (location == -1) return;
        glUniform1i(location, value ? 1 : 0);
    }
    public void setUniform(String name, Matrix4f value) {
        try (MemoryStack stack = MemoryStack.stackPush()) {
            FloatBuffer buffer = stack.mallocFloat(16);
            value.get(buffer);
            int location = glGetUniformLocation(programId, name);
            if (location == -1) return;
            glUniformMatrix4fv(location, false, buffer);
        }
    }
    public void cleanup() {
        glDeleteProgram(programId);
    }
}